//: net/mindview/atunit/AtUnit.java
// An annotation-based unit-test framework.
// {RunByHand}
package net.mindview.atunit;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class AtUnit implements ProcessFiles.Strategy {
  static Class<?> testClass;//测试类型
  static List<String> failedTests= new ArrayList<String>();

  //测试计数
  static long testsRun = 0;
  static long failures = 0;

  public static void main(String[] args) throws Exception {
    ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true); // Enable asserts
    new ProcessFiles(new AtUnit(), "class").start(args);
    if(failures == 0)
      print("OK (" + testsRun + " tests)");
    else {
      print("(" + testsRun + " tests)");
      print("\n>>> " + failures + " FAILURE" +
        (failures > 1 ? "S" : "") + " <<<");
      for(String failed : failedTests)
        print("  " + failed);
    }
  }

  /**
   * 拿到一个经过ProcessFile后的file，怎么处理
   * @param cFile
   */
  public void process(File cFile) {
    //拿到测试类 类型(Class)
    try {
      //这里解决一个问题，我们拿到一个file文件后，可能引用类名并不是文件名
      //这时候需要解析字节码，从字节码分析中获取类名
      String cName = ClassNameFinder.thisClass(BinaryFile.read(cFile));
      if(!cName.contains("."))
        return; // Ignore unpackaged classes
      testClass = Class.forName(cName);
    } catch(Exception e) {
      throw new RuntimeException(e);
    }

    TestMethods testMethods = new TestMethods();
    Method creator = null;
    Method cleanup = null;
    //逐个检测 测试实例的方法，是@Test @create @cleanup
    for(Method m : testClass.getDeclaredMethods()) {
      testMethods.addIfTestMethod(m);
      if(creator == null)
        creator = checkForCreatorMethod(m);
      if(cleanup == null)
        cleanup = checkForCleanupMethod(m);
    }
    if(testMethods.size() > 0) {
      //初始化构造的静态方法不在且构造非公，则异常退出
      if(creator == null)
        try {
          if(!Modifier.isPublic(testClass
             .getDeclaredConstructor().getModifiers())) {
            print("Error: " + testClass +
              " default constructor must be public");
            System.exit(1);
          }
        } catch(NoSuchMethodException e) {
          // Synthesized default constructor; OK
        }
      print(testClass.getName());
    }

    for(Method m : testMethods) {
      printnb("  . " + m.getName() + " ");
      try {
        //创建测试类实例
        Object testObject = createTestObject(creator);
        boolean success = false;

        //测试方法的调用 Boolean返回true/false void直接true(没有断言失败情况下，失败走异常)
        // 如果测试失败,则打印异常，默认返回false
        try {
          if(m.getReturnType().equals(boolean.class))
            success = (Boolean)m.invoke(testObject);
          else {
            m.invoke(testObject);
            success = true; // If no assert fails
          }
        } catch(InvocationTargetException e) {
          // Actual exception is inside e:
          print(e.getCause());
        }

        print(success ? "" : "(failed)");
        testsRun++;
        if(!success) {
          failures++;
          failedTests.add(testClass.getName() +
            ": " + m.getName());
        }
        if(cleanup != null)
          cleanup.invoke(testObject, testObject);
      } catch(Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  /**
   * 检索方法注解是否为@Test->检索返回类型是否为boolean/void
   * ->是则加入链表中
   */
  static class TestMethods extends ArrayList<Method> {
    void addIfTestMethod(Method m) {
      if(m.getAnnotation(Test.class) == null)
        return;
      if(!(m.getReturnType().equals(boolean.class) ||
          m.getReturnType().equals(void.class)))
        throw new RuntimeException("@Test method" +
          " must return boolean or void");
      m.setAccessible(true); // In case it's private, etc.
      add(m);
    }
  }

  /**
   * 检索测试方法是否有@TestObjectCreate->
   * 检索返回类型是否为测试类型->
   * 试是否为静态static->
   * 打开权限
   * @param m
   * @return
   */
  private static Method checkForCreatorMethod(Method m) {
    //检索测试方法是否有@TestObjectCreate
    if(m.getAnnotation(TestObjectCreate.class) == null)
      return null;
//    检索返回类型是否为测试类型
    if(!m.getReturnType().equals(testClass))
      throw new RuntimeException("@TestObjectCreate " +
        "must return instance of Class to be tested");
    //试是否为静态static
    if((m.getModifiers() &
         java.lang.reflect.Modifier.STATIC) < 1)
      throw new RuntimeException("@TestObjectCreate " +
        "must be static.");
    m.setAccessible(true);
    return m;
  }

  /**
   * 测试是否为@TestObjectCleanup，验证注解
   * @param m
   * @return
   */
  private static Method checkForCleanupMethod(Method m) {
    if(m.getAnnotation(TestObjectCleanup.class) == null)
      return null;
    //返回类型是否void
    if(!m.getReturnType().equals(void.class))
      throw new RuntimeException("@TestObjectCleanup " +
        "must return void");
    //限定符是否static
    if((m.getModifiers() &
        java.lang.reflect.Modifier.STATIC) < 1)
      throw new RuntimeException("@TestObjectCleanup " +
        "must be static.");
    //是否有参数且有testClass类型
    if(m.getParameterTypes().length == 0 ||
       m.getParameterTypes()[0] != testClass)
      throw new RuntimeException("@TestObjectCleanup " +
        "must take an argument of the tested type.");
    //为private开权限
    m.setAccessible(true);
    return m;
  }

  /**
   * 使用反射创建测试类型实例
   * @param creator
   * @return
   */
  private static Object createTestObject(Method creator) {
    //通过构造的静态方法，构造对象
    if(creator != null) {
      try {
        return creator.invoke(testClass);
      } catch(Exception e) {
        throw new RuntimeException("Couldn't run " +
          "@TestObject (creator) method.");
      }
    } else { // Use the default constructor:
      try {
        //直接使用默认的构造方法实例化类型
        return testClass.newInstance();
      } catch(Exception e) {
        throw new RuntimeException("Couldn't create a " +
          "test object. Try using a @TestObject method.");
      }
    }
  }
} ///:~
